import sys, os
import struct
from threading import Thread, Lock

sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
#from bitbrook.utilities.limited_socket import LSocket
from bitbrook.seed_worker import SeedWorker
from bitbrook.network_builder import InfoSender
import socket

#a tuple of a 16byte long string for the command and a int of
#the following data transmitted.
COMMAND_FORMAT = "!16sI"

#this mechanics is intended to be in the peer_facade main loop, but for now
#I want to keep it here for debug reasons.
class ConnectionHandler(Thread):
    def __init__(self, addr, brook, network_builder, network):
        super(ConnectionHandler, self).__init__()
        #self._listen_sock = LSocket()
        self._listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self._listen_sock.bind(addr)
        self._listen_sock.listen(5)
        #needed for thread stopping
        self._shutdown = False
        self._shutdown_signal = Lock()
        self._brook = brook
        self._builder = network_builder
        self._network = network
        
    def run(self):
        with self._shutdown_signal:
            while not self._shutdown:
                self._listen_sock.settimeout(10)
                try:
                    (sock, _) = self._listen_sock.accept()
                    self._listen_sock.settimeout(0)
                    data = self._recieve(sock, 0)
                    # Check whether the connection has been closed.
                    if data == "":
                        break
                    # Unpack the data.
                    try:
                        cmd, payload = struct.unpack(COMMAND_FORMAT, data)
                    except:
                        # Error unpacking command - wrong format. Send an error message back.
                        self.nack(sock, "Invalid command format.")
                        continue
                    cmd = cmd.strip("\0").upper()
                    if cmd == "REQUEST":
                        self.ack(sock)
                        if "ACK" == self._recieve(sock, 0):
                            seed = SeedWorker(sock, payload, self._brook)
                            seed.start()
                    elif cmd == "GOSSIP":
                        info_sender = InfoSender(self._network, self._brook, sock, payload)
                        info_sender.start()
                    else:
                        # Unknown command.
                        self.nack(sock, "Unknown command %s"%cmd)
                        continue
                except:
                    pass

    def nack(self, sock, reason = ""):
        send = "NCK %s"%reason 
        self._send(sock, send)

    def ack(self, sock):
        self._send(sock, "ACK")
    
    def shutdown(self, block = True):
        print "Shutting down Connection Handler"
        self._shutdown = True
        self._listen_sock.close()
        if block:
            self._shutdown_signal.acquire()
            self._shutdown_signal.release()
    
    def _send(self, sock, data):
        sent = 0
        while sent < len(data):
            sent += sock.send(data[sent:])
    
    def _recieve(self, sock, leng):
        if leng == 0:
            tries = 0
            while tries < 4:
                try:
                    return sock.recv(1024)
                except BaseException as e:
                    tries += 1
                    if tries == 4:
                        raise e
        else:
            data = ""
            while len(data) < leng:
                data = data + sock.recv(1024)
            return data